#include "Light.h"
#include "result.h"
#include "material.h"
#include "result.h"
#include "sampler.h"
#include "Ray.h"
#include "World.h"
#include "GlobalDefine.h"

namespace mray{

	vec3 PointLight::Illuminate(const Result& result)const{
		vec3 wi = m_pos - result.p;
		//length from light to object
		float dis = wi.length();
		wi.normalize();
		//if case shadow
		if(m_castShadow){
			//there is object between light and object
			Ray shadowRay(result.p, wi);
			result.m_pWorld->Hit( shadowRay );
			if( shadowRay.m_mint < dis) return BLACK;
		}
		//start shade
		return result.pMtl->Shade(wi, m_color * m_atten.CalcAtten(dis), result);
	}

	AmbientLight::AmbientLight(const vec3& color, int sub):BaseLight(color, true)
	{
		m_color = color;
		m_sampler = new HemisphereSampler(new AverageSampler);
		m_sampler->GenerateSamples(sub, 1, 0.f);
	}

	AmbientLight::~AmbientLight()
	{
		delete this->m_sampler;
	}

	mray::vec3 AmbientLight::Illuminate(const Result& result) const
	{
		const vec3& zaxis = result.n;
		vec3 xaxis(1.0, 0.0, 0.0);
		vec3 yaxis = zaxis % xaxis;
		xaxis = yaxis % zaxis;
		xaxis.normalize();
		yaxis.normalize();

		vec3 color = BLACK;
		for(int i = 0; i < m_sampler->nSamples; i++){
			vec3 xyz = m_sampler->GetSamples(i);
			vec3 dir = xaxis * xyz.x + yaxis * xyz.y + zaxis * xyz.z;
			Ray shadowRay(result.p, dir);
			bool ret = result.m_pWorld->Hit(shadowRay);
			if(ret && m_castShadow) continue;
			color += m_color ^ result.pMtl->Shade(dir, m_color, result);
		}
		color /= (float)m_sampler->nSamples;
		return color;
	}

	mray::vec3 DirectionalLight::Illuminate(const Result& result) const
	{
		if(m_castShadow){
			Ray shadowRay(result.p, m_dir);
			if(result.m_pWorld->Hit(shadowRay))
				return BLACK;
		}
		return result.pMtl->Shade(m_dir, m_color, result);
	}

	DirectionalLight::DirectionalLight(const vec3& color, const vec3& dir, bool castShadow):
		BaseLight(color,castShadow), m_dir(-dir)
	{
			m_dir.normalize();
	}


}